#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <gmp.h>
#include <gmp-x86_64.h>
#include <string.h>
#include <pthread.h>

#define SEED 35791246

void *calcularPontos();
void calcularPI(unsigned long int count);
void incrementarContador(void);

pthread_mutex_t mutexPontos = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutexPI = PTHREAD_MUTEX_INITIALIZER;

mpf_t pi, novoPi, niter, niterTmp, maxRandom;

void main(void)
{
   
   pthread_t thread_id[2];
   int i;
   
   mpf_init2(pi,100);
   mpf_init2(novoPi,100);
   mpf_init2(niter,10);
   mpf_init2(niterTmp,10);
   mpf_init2(maxRandom,10);
   
   mpf_set_ui(maxRandom,(unsigned long int) RAND_MAX);
   mpf_set_ui(niter,(unsigned long int) 1);
   
   for(i = 0; i < 2; i++){
        pthread_create( &thread_id[i], NULL, calcularPontos, NULL );
   }
   
   for(i = 0; i < 2; i++){
       pthread_join( thread_id[i], NULL);
   }
   
   mpf_out_str(stdout,10,0,pi);
   
   exit(0);
}

void *calcularPontos(){
   unsigned long int count = 0; 
   
   mpf_t x, y, z, divX, divY;
   mp_bitcnt_t prec;
   mp_bitcnt_t maxPrec = 130;
   
   mpf_init2(x,10);      
   mpf_init2(y,10);
   mpf_init2(z,10);
   mpf_init2(divX,10);
   mpf_init2(divY,10);
   
   /* initialize random numbers */
   srand(SEED);
   
   while(prec < maxPrec){
       
      //divX = rand()/maxRandom;
      mpf_ui_div(divX, (unsigned long int)rand(), maxRandom);
      
      //divY = rand()/maxRandom;
      mpf_ui_div(divY, (unsigned long int)rand(), maxRandom);
      
      mpf_set( x , divX);
      mpf_set( y , divY);
      
      //x = x*x
      mpf_mul(x,x,x);
      
      //y = y*y
      mpf_mul(y,y,y);
      
      mpf_add(z,x,y);
      
      if (mpf_cmp_d(z,(double)1) < 0) {
          count++;
      
            //Entra na região crítica
            pthread_mutex_lock( &mutexPI );
                calcularPI(count);
            //Sai da região crítica
            pthread_mutex_unlock( &mutexPI );
      }
       
      //Entra na região crítica 
      pthread_mutex_lock( &mutexPontos );
        incrementarContador();
      ////Sai da região crítica
      pthread_mutex_unlock( &mutexPontos );
      
      prec = mpf_get_prec(pi);

   }
   return NULL;
}

void calcularPI(unsigned long int count){
    
    //novoPi = 4*count/niterTmp;
    mpf_ui_div(novoPi, 4*count, niter);
    
    mpf_set( pi , novoPi);
    
    mpf_out_str(stdout,10,0,pi);
    printf("\n");
    
}

void incrementarContador(void){
    
    mpf_add_ui(niter, niter, (unsigned long int) 1);
    
}

